package com.coolingme.controller.alipay;

import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayClient;
import com.alipay.api.domain.*;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import lombok.extern.slf4j.Slf4j;
import com.coolingme.enums.ResponseEnum;
import com.coolingme.properties.AlipayProperties;
import com.coolingme.util.DefaultAlipayUtil;
import com.coolingme.util.ResponseUtil;
import com.coolingme.web.pay.alipay.AlipayUtil;
import com.coolingme.web.pay.alipay.DefaultAlipayClientFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLDecoder;
import java.net.URLEncoder;

/**
 * 电脑网站支付<br>
 * https://docs.open.alipay.com/270/105900/
 *
 * @author: 王越
 * @date: 2019-06-21 01:03:17
 */
@Slf4j
@Api(tags = "支付宝电脑网站支付")
@ApiSupport(order = 601, author = "wangyue")
@ApiResponses({@ApiResponse(code = 200, message = "Request Success"),
        @ApiResponse(code = 401, message = "Authentication Failure"),
        @ApiResponse(code = 402, message = "Login Information Invalid"),
        @ApiResponse(code = 403, message = "No Permission"), @ApiResponse(code = 500, message = "Request fail")})
@Controller
@RequestMapping("/alipay")
public class AlipayController {

    @ResponseBody
    @RequestMapping(value = "/readAlipay", method = RequestMethod.GET)
    public void readAlipay() {
        AlipayClient alipayClient = DefaultAlipayClientFactory.getAlipayClient();
        System.out.println(alipayClient);
    }

    /**
     * alipay.trade.query (统一收单线下交易查询)
     * api:https://docs.open.alipay.com/api_1/alipay.trade.query
     * demo:https://openclub.alipay.com/club/history/read/13842
     *
     * @param model            交易查询请求参数类
     * @param model.outTradeNo 商户订单号
     * @param model.tradeNo    支付宝交易号
     * @param model.orgPid     所属收单机构的pid,银行间联模式下有用
     * @return 交易查询结果
     */
    @ResponseBody
    @RequestMapping(value = "/query", method = RequestMethod.POST)
    public JSONObject query(@RequestBody AlipayTradeQueryModel model) {
        log.info("*********************交易查询***************************");
        JSONObject strj = null;
        try {
            strj = AlipayUtil.query(model);
//			int code = strj.getIntValue("code");
//			String outTradeNo = strj.getString("out_trade_no");
//			double totalAmount = strj.getDoubleValue("total_amount");
        } catch (Exception e) {
            // TODO: handle exception
        }
        return strj;
    }

    /**
     * alipay.trade.close(统一收单交易关闭接口)
     * api:https://docs.open.alipay.com/api_1/alipay.trade.close
     * demo:https://openclub.alipay.com/club/history/read/13963
     *
     * @param model            交易关闭请求参数类
     * @param model.outTradeNo 商户订单号
     * @param model.tradeNo    支付宝交易号
     * @return 交易关闭结果
     */
    @ResponseBody
    @RequestMapping(value = "/close", method = RequestMethod.POST)
    public JSONObject close(@RequestBody AlipayTradeCloseModel model) {
        log.info("*********************交易关闭***************************");
        JSONObject strj = null;
        try {
            strj = AlipayUtil.close(model);
        } catch (Exception e) {
            // TODO: handle exception
        }
        return strj;
    }

    /**
     * alipay.trade.refund(统一收单交易退款接口)
     * api:https://docs.open.alipay.com/api_1/alipay.trade.refund
     * demo:https://openclub.alipay.com/club/history/read/13853
     *
     * @param model              退款请求参数类
     * @param model.outTradeNo   商户订单号
     * @param model.tradeNo      支付宝交易号
     * @param model.refundAmount 退款的金额
     * @param model.refundReason 退款的原因说明
     * @param model.outRequestNo 商户订单号
     * @return 退款结果
     */
    @ResponseBody
    @RequestMapping(value = "/refund", method = RequestMethod.POST)
    public JSONObject refund(@RequestBody AlipayTradeRefundModel model) {
        log.info("*********************退款***************************");
        JSONObject strj = null;
        try {
            model.setOutRequestNo(model.getOutTradeNo() + 'R' + Math.random());
            strj = AlipayUtil.refund(model);
        } catch (Exception e) {
            // TODO: handle exception
        }
        return strj;
    }

    /**
     * alipay.trade.fastpay.refund.query(统一收单交易退款查询)
     * api:https://docs.open.alipay.com/api_1/alipay.trade.fastpay.refund.query
     * demo:https://openclub.alipay.com/club/history/read/13962
     *
     * @param model              退款请求参数类
     * @param model.outTradeNo   商户订单号
     * @param model.tradeNo      支付宝交易号
     * @param model.outRequestNo 商户订单号
     * @return 退款结果
     */
    @ResponseBody
    @RequestMapping(value = "/refundQuery", method = RequestMethod.POST)
    public JSONObject refundQuery(@RequestBody AlipayTradeFastpayRefundQueryModel model) {
        log.info("*********************退款查询***************************");
        JSONObject strj = null;
        try {
            strj = AlipayUtil.refundQuery(model);
        } catch (Exception e) {
            // TODO: handle exception
        }
        return strj;
    }

    /**
     * alipay.data.dataservice.bill.downloadurl.query(查询对账单下载地址)
     * api:https://docs.open.alipay.com/api_15/alipay.data.dataservice.bill.downloadurl.query
     * demo:https://openclub.alipay.com/club/history/read/14016
     * <p>
     * （1）bill_type：账单类型，可设置参数类型：trade、signcustomer；<br>
     * trade指商户基于支付宝交易收单的业务账单；<br>
     * signcustomer是指基于商户支付宝余额收入及支出等资金变动的帐务账单；<br>
     * （2）bill_date：账单时间：日账单格式为yyyy-MM-dd，月账单格式为yyyy-MM；
     *
     * @param model          查询对账单下载地址请求参数类
     * @param model.billType 账单类型
     * @param model.billDate 账单时间
     * @return 退款结果
     */
    @ResponseBody
    @RequestMapping(value = "/billQuery", method = RequestMethod.POST)
    public JSONObject billQuery(@RequestBody AlipayDataDataserviceBillDownloadurlQueryModel model) {
        log.info("*********************查询对账单下载地址***************************");
        JSONObject strj = null;
        try {
            strj = AlipayUtil.billQuery(model);
        } catch (Exception e) {
            // TODO: handle exception
        }
        return strj;
    }

    /**
     * alipay.trade.page.pay(统一收单下单并支付页面接口)
     * api:https://docs.open.alipay.com/api_1/alipay.trade.page.pay/
     * demo:https://openclub.alipay.com/club/history/read/6361
     *
     * @param model             电脑网站支付请求参数类
     * @param model.outTradeNo  商户订单号
     * @param model.totalAmount 付款金额
     * @param model.subject     订单名称
     * @param model.body        商品描述
     * @return 支付宝提交表单脚本
     */
    @ResponseBody
    @RequestMapping(value = "/pagePay", method = RequestMethod.POST)
    public String pagePay(@RequestBody AlipayTradePagePayModel model) {
        log.info("*********************支付宝电脑网站支付--Ajax***************************");
        String form = null;
        try {
            // 设置销售产品码
            model.setProductCode(AlipayProperties.PRODUCT_CODE_PAGE);
            // 该笔订单允许的最晚付款时间，逾期将关闭交易。
            model.setTimeoutExpress("10m");

//			model.setQrPayMode("1");

            // 自定义参数请转码后传入该参数内，可以传值逗号，等号之类，但不能传引号，否则会报错，其他特殊符号是否报错以实测为准
            String params = "{testParam=测试一下公共参数;name=Dylan.W;age=18}";
            // 本参数必须进行UrlEncode之后才可以发送给支付宝。 支付宝只会在同步返回（包括跳转回商户网站）和异步通知时将该参数原样返回。
            String passbackParams = URLEncoder.encode(params, "UTF-8");
            // 公用回传参数，如果请求时传递了该参数，则返回给商户时会回传该参数。
            model.setPassbackParams(passbackParams);
            form = AlipayUtil.pagePay(model);
        } catch (Exception e) {
            e.printStackTrace();
            // 抛一个自定义支付宝异常
            return ResponseUtil.error(ResponseEnum.ALIPAY_ERROR.code(), "支付宝支付失败");
        }
        return ResponseUtil.success(form);
    }

    /**
     * 页面跳转同步通知页面路径
     *
     * @param returnPay
     * @param response
     * @param request
     */
    @RequestMapping(value = "/pageReturn", method = RequestMethod.GET)
    public void pageReturn(HttpServletResponse response, HttpServletRequest request) throws IOException {
        response.setContentType("type=text/html;charset=UTF-8");
        log.info("****************************************支付宝的的电脑网站同步回调函数被调用******************************");
        // 商户订单号
        String outTradeNo = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"), "UTF-8");
        // 付款金额
        String totalAmount = new String(request.getParameter("total_amount").getBytes("ISO-8859-1"), "UTF-8");
        // 验签
        boolean verifyResult = DefaultAlipayUtil.checkSign(request, "pageReturn");
        if (verifyResult) {
            log.info("*******************************电脑网站同步验签成功*****************************");
            log.info("***********************执行具体业务逻辑***********************");
            // 以下为具体业务逻辑

            // 页面重定向至成功页面
            response.sendRedirect(AlipayProperties.RESPONSE_UTL_PAGE);
        } else {
            log.info("*******************************电脑网站同步验签失败*****************************");
            response.getWriter().write("failture");
            return;
        }

    }

    /**
     * 支付宝的异步通知页面路径<br>
     * 支付宝签约商户时有限制，必须在签约的域名上测试支付宝的支付结果。<br>
     * 如果直接返回商户的则不需要服务器上调试，但是支付宝的异步通知，必须要在公网上才可以调试。
     *
     * @param response
     * @param request
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/pageNotify", method = RequestMethod.POST)
    public String pageNotify(HttpServletResponse response, HttpServletRequest request) {
        // 获取支付宝POST过来反馈信息
        log.info("****************************************支付宝的电脑网站异步回调函数被调用******************************");
        /*
         * 实际验证过程建议商户务必添加以下校验： 1、需要验证该通知数据中的outTradeNo是否为商户系统中创建的订单号，
         * 2、判断totalAmount是否确实为该订单的实际金额（即商户订单创建时的金额）， 3、校验通知中的seller_id（或者seller_email)
         * 是否为outTradeNo这笔单据的对应的操作方（有的时候，一个商户可能有多个seller_id/seller_email）
         * 4、验证app_id是否为该商户本身。
         */
        // 验签
        boolean verifyResult = DefaultAlipayUtil.checkSign(request, "pageNotify");
        if (verifyResult) {
            log.info("*************************电脑网站异步验签成功*******************************");
            try {
                // 获取需要的数据，支付宝返回参数为下划线分割

                // 商户订单号
                String outTradeNo = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"), "UTF-8");
                // 付款金额
                String totalAmount = new String(request.getParameter("total_amount").getBytes("ISO-8859-1"), "UTF-8");
                // 支付宝交易号
                String tradeNo = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"), "UTF-8");
                // 交易状态
                String trade_status = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"), "UTF-8");

                try {
                    // 自定义参数
                    String passbackParams = new String(request.getParameter("passback_params").getBytes("ISO-8859-1"),
                            "UTF-8");
                    String params = URLDecoder.decode(passbackParams, "UTF-8");
                } catch (Exception e) {
                    // TODO: handle exception
                }

                log.info("电脑网站交易状态:" + trade_status);

                // 设置支付宝订单状态
                switch (trade_status) {
                    case "TRADE_FINISHED": // 交易结束并不可退款
                        break;
                    case "TRADE_SUCCESS": // 交易支付成功


                        break;
                    case "TRADE_CLOSED": // 未付款交易超时关闭或支付完成后全额退款
                        break;
                    case "WAIT_BUYER_PAY": // 交易创建并等待买家付款
                        break;
                    default:
                        break;
                }

                if (trade_status.equals("TRADE_FINISHED")) {
                    // 判断该笔订单是否在商户网站中已经做过处理
                    // 如果没有做过处理，根据订单号（outTradeNo）在商户网站的订单系统中查到该笔订单的详细，并执行商户的业务程序
                    // 如果有做过处理，不执行商户的业务程序

                    // 注意：
                    // 退款日期超过可退款期限后（如三个月可退款），支付宝系统发送该交易状态通知
                } else if (trade_status.equals("TRADE_SUCCESS")) {
                    // 判断该笔订单是否在商户网站中已经做过处理
                    // 如果没有做过处理，根据订单号（outTradeNo）在商户网站的订单系统中查到该笔订单的详细，并执行商户的业务程序
                    // 如果有做过处理，不执行商户的业务程序

                    // 注意：
                    // 付款完成后，支付宝系统发送该交易状态通知

                    // 执行业务逻辑
                    // 通过商户订单号查找订单，判断其当前的状态

                    // 更新支付宝订单状态

                    // 查询交易信息
//					AlipayTradeQueryModel model = new AlipayTradeQueryModel();
//					model.setOutTradeNo(outTradeNo);
//					model.setTradeNo(tradeNo);
//					JSONObject strj = AlipayUtil.query(model);
//					int code = strj.getIntValue("code");
//					if (10000 == code) {
//						// 查询成功
//					}

                } else {
                    // 若是其他状态，则为失败
                    return "fail";
                }
            } catch (Exception e) {
                e.printStackTrace();
                return "fail";
            }

            return "success";

        } else {// 验证失败
            log.info("*************************电脑网站异步验签失败*******************************");
            return "fail";
        }

    }

}
